﻿using Quartz;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;

namespace Hinacom.Scheduler
{
    /// <summary>
    /// 任务执行信息基类。用来指定执行任务的参数、计划安排等。
    /// </summary>
    [KnownType("GetKnwonJobInfoTypes")]
    public abstract class JobInfo
    {
        private JobParameter _parameter;
        private DateTime? _startTime;
        private DateTime? _endTime;

        #region Properties: Job Info
        /// <summary>
        /// 任务执行信息名称。同一个组内的每一个任务执行信息应指定唯一的名称。
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 该任务执行信息从属的组。
        /// </summary>
        public string Group { get; set; }
        /// <summary>
        /// 描述信息。
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 任务执行程序的配置名称。该名称应与配置文件中的scheduler\jobtypes下的一个jobtype配置项对应。
        /// </summary>
        public string JobTypeName { get; set; }
        /// <summary>
        /// 任务执行时所用的参数。具体需要哪些参数由配置的任务执行程序决定。
        /// </summary>
        public JobParameter Parameter
        {
            get
            {
                if (_parameter == null)
                    _parameter = new JobParameter();
                return _parameter;
            }
        }
        #endregion

        #region Properties: Schedule
        /// <summary>
        /// 开始执行时间。根据其他条件设置，任务不一定会在这个时间首次执行，但不会早于这个时间执行。
        /// 应传入DateTimeKind为Utc的时间。若传入日期的DateTimeKind为Local，会转化成相应UTC时间。若传入日期的DateTimeKind为Unspecified，会将其当作UTC时间处理。
        /// </summary>
        public DateTime? StartTimeUtc
        {
            get { return this._startTime; }
            set
            {
                if (value.HasValue)
                    _startTime = ForceUtcTime(value.Value);
                else
                    _startTime = null;
            }
        }
        /// <summary>
        /// 结束执行时间。根据其他条件设置，任务不一定会在这个时间首次执行，但晚于这个时间任务不会执行。
        /// 应传入DateTimeKind为Utc的时间。若传入日期的DateTimeKind为Local，会转化成相应UTC时间。若传入日期的DateTimeKind为Unspecified，会将其当作UTC时间处理。
        /// </summary>
        public DateTime? EndTimeUtc
        {
            get { return this._endTime; }
            set
            {
                if (value.HasValue)
                    _endTime = ForceUtcTime(value.Value);
                else
                    _endTime = null;
            }
        }
        /// <summary>
        /// 将时间强制转化为Utc时间。若传入日期的DateTimeKind为Local，会转化成相应UTC时间。若传入日期的DateTimeKind为Unspecified，会将其当作UTC时间处理。
        /// </summary>
        private DateTime ForceUtcTime(DateTime time)
        {
            DateTime dt = default(DateTime);
            switch (time.Kind)
            {
                case DateTimeKind.Utc:
                    dt = time;
                    break;
                case DateTimeKind.Unspecified:
                    dt = DateTime.SpecifyKind(time, DateTimeKind.Utc);
                    break;
                case DateTimeKind.Local:
                    dt = time.ToUniversalTime();
                    break;
            }
            return dt;
        }
        #endregion

        #region 这部分内容用于保证调用WCF API时可以正确的识别JobInfo的子类
        private static IEnumerable<Type> _knownJobInfoTypes;
        private static IEnumerable<Type> GetKnwonJobInfoTypes()
        {
            if (_knownJobInfoTypes == null)
                _knownJobInfoTypes = Assembly.GetExecutingAssembly()
                                        .GetTypes()
                                        .Where(t => typeof(JobInfo).IsAssignableFrom(t))
                                        .ToList();
            return _knownJobInfoTypes;
        }
        #endregion
    }
    /// <summary>
    /// Run Once或简单重复的任务执行计划。
    /// </summary>
    [JobInfoConverter(Type = typeof(SimpleJobInfoConverter))]
    public class SimpleJobInfo : JobInfo
    {
        public SimpleJobInfo()
        {
        }

        private const long DEFAULT_INTERVAL_MILLISECONDS = 60000; // default interval: 1 minite

        private TimeSpan? _interval;
        private int? _reportCount;
        /// <summary>
        /// 重复间隔。
        /// </summary>
        public TimeSpan Interval
        {
            get
            {
                if (_interval.HasValue)
                    return _interval.Value;
                return new TimeSpan(DEFAULT_INTERVAL_MILLISECONDS * 10000);
            }
            set
            {
                _interval = value;
            }
        }
        /// <summary>
        /// 重复执行次数。等于0代表执行一次（不重复）；大于0会执行ReportCount + 1次；小于0代表不限制次数，有效期内一直重复。
        /// </summary>
        public int RepeatCount
        {
            get
            {
                if (_reportCount.HasValue)
                    return _reportCount.Value;
                return 0; // run once
            }
            set
            {
                _reportCount = value;
            }
        }
    }
    /// <summary>
    /// 每天固定时间段执行，或每天固定执行次数的任务。
    /// </summary>
    [JobInfoConverter(Type = typeof(DailyJobInfoConverter))]
    public class DailyJobInfo : JobInfo
    {
        public DailyJobInfo() {}

        private const long MIN_INTERVAL_MILLISECONDS = 100; // min interval: 100 milliseconds
        private const long DEFAULT_INTERVAL_MILLISECONDS = 60000; // default interval: 1 minite

        private TimeSpan? _interval;
        private TimeSpan? _endDailyAt;
        private int? _endDailyAfterCount;

        /// <summary>
        /// 每天的开始执行时间。
        /// </summary>
        public TimeSpan StartDailyAt { get; set; }
        /// <summary>
        /// 每天的结束执行时间。超过此时间当天不会再执行。
        /// 此属性与EndDailyAfterCount互斥。当设置此值时，EndDailyAfterCount会被清空。反之亦然。
        /// </summary>
        public TimeSpan? EndDailyAt
        {
            get { return this._endDailyAt; }
            set
            {
                this._endDailyAt = value;
                if (value.HasValue)
                    this._endDailyAfterCount = null;
            }
        }
        /// <summary>
        /// 每天执行的次数。若设置为小于1的数据，会被当成1处理。
        /// 此属性与EndDailyAt互斥。当设置此值时，EndDailyAt会被清空。反之亦然。
        /// </summary>
        public int? EndDailyAfterCount {
            get { return this._endDailyAfterCount; }
            set
            {
                if (value.HasValue)
                {
                    if (value.Value < 1)
                        this._endDailyAfterCount = 1;
                    else
                        this._endDailyAfterCount = value;
                    this._endDailyAt = null;
                }
                else
                    this._endDailyAfterCount = null;   
            }
        }
        /// <summary>
        /// 指定哪些天需要执行。
        /// </summary>
        public DayOfWeek[] RunOnWeekDays { get; set; }
        /// <summary>
        /// 重复间隔。
        /// </summary>
        public TimeSpan Interval
        {
            get
            {
                if (_interval.HasValue)
                    return _interval.Value;
                return new TimeSpan(DEFAULT_INTERVAL_MILLISECONDS * 10000);
            }
            set
            {
                _interval = value;
            }
        }
    }
    /// <summary>
    /// 
    /// </summary>
    [JobInfoConverter(Type = typeof(CalendarJobInfoConverter))]
    public class CalendarJobInfo : JobInfo
    {
        public CalendarJobInfo()
        {
        }
    }

}
